Skip to content

fix(connect): complete Standalone Connect logins - #111

Merged
gjtorikian merged 2 commits into
mainfrom
workgraph/emulate-74c-d7895e59
Sep 15, 2026
Merged

gjtorikian merged 2 commits into
mainfrom
workgraph/emulate-74c-d7895e59

Conversation

@gjtorikian

Copy link
Copy Markdown
Collaborator

Summary

  • Implement API-key-protected POST /authkit/oauth2/complete, upserting users by external ID and emitting user lifecycle events so Standalone Connect clients can finish login.
  • Add the minimal browser authorize/completion redirects and authorization-code token exchange, using a seedable connectApplications[].login_url to exercise the whole flow locally.
  • Distinguish completed sessions from unknown/expired sessions; enforce single-use redirects/codes, client-secret and callback binding, and rejection of Connect codes by AuthKit's separate authentication endpoint.
  • Add regression coverage and document setup and deliberate divergences; regenerate SUPPORTED.md (Applications write coverage: 5/8).

Validation

  • bun test: 1,206 passing tests, zero failures.
  • bun run typecheck, bun run lint, bun run fmt:check, and bun run build: passed.
  • bun run gen:events, bun run gen:shapes, and bun run gen:supported: generated catalogs and support matrix are current.
  • Node library/package smoke checks passed; live HTTP curl verified the complete authorize-to-token flow, completion replay, redirect replay, and unknown/expired IDs.

Limitations

No PKCE, refresh/ID tokens, consent UI, or email_change_not_allowed policy. user_consent_options and authorize-time scopes are not modeled. Provisioned emails are marked verified, following the emulator's SSO precedent. Follow the returned completion URL rather than assuming the production URL shape.

An independent review passed with one non-blocking advisory: malformed YAML login_url values are not checked by the seed config validator (API creation does validate the field's type).

Fixes #109

Standalone login clients could not finish authentication because the
completion endpoint was missing, making unsupported routes look like
expired sessions. Provide the minimal authorize-to-token loop so tests
can exercise successful logins and distinguish expiry from completion
replay, while keeping Connect codes out of AuthKit's separate exchange.

Fixes #109
@greptile-apps

greptile-apps Bot commented Sep 11, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

The PR appears safe to merge, with only the existing non-blocking seeded login URL validation concern remaining.

Findings

  1. P2 Seeded login URLs go unchecked
Fix with agent prompt
### Issue 1
src/workos/index.ts:undefined-755
The new seeded `login_url` is stored without validation, so malformed values are accepted during startup and fail only when `/oauth2/authorize` returns `400 invalid_redirect_uri`. Validating this field with the other `connectApplications` settings would report configuration errors when the seed is loaded.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

This PR implements the Standalone Connect authorization flow:

  • Adds API-key-protected external-auth completion with user upsert and lifecycle events.
  • Adds browser authorization/completion redirects and single-use authorization-code exchange.
  • Binds codes to the OAuth client, client secret, callback URI, flow type, and expiration.
  • Adds regression coverage, seed configuration, documentation, and generated support metadata.
  • The latest changes add type validation for seeded login_url values.

Diagram

sequenceDiagram
  participant Browser
  participant Emulator
  participant LoginApp as Application Login
  participant Backend as Application Backend
  participant Callback as Client Callback

  Browser->>Emulator: GET /oauth2/authorize
  Emulator->>Emulator: Create external-auth session
  Emulator-->>Browser: "302 login_url?external_auth_id=..."
  Browser->>LoginApp: Authenticate user
  LoginApp->>Backend: Authenticated identity
  Backend->>Emulator: POST /authkit/oauth2/complete
  Emulator->>Emulator: Upsert user and complete session
  Emulator-->>Backend: Completion redirect_uri
  Browser->>Emulator: GET /oauth2/authorize/complete
  Emulator->>Emulator: Redeem session and create code
  Emulator-->>Browser: "302 callback?code=...&state=..."
  Browser->>Callback: Authorization code
  Backend->>Emulator: POST /oauth2/token
  Emulator->>Emulator: Validate and consume code
  Emulator-->>Backend: Access token
Loading

Reviews (2) · Last reviewed commit: "fix(seed): type-check connectApplication..."

Comment thread src/workos/index.ts
scopes: appConfig.scopes ?? [],
audience: appConfig.audience ?? null,
redirect_uris: appConfig.redirect_uris ?? [],
login_url: appConfig.login_url ?? null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Seeded login URLs go unchecked

The new seeded login_url is stored without validation, so malformed values are accepted during startup and fail only when /oauth2/authorize returns 400 invalid_redirect_uri. Validating this field with the other connectApplications settings would report configuration errors when the seed is loaded.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/workos/index.ts
Line: 755

Comment:
**Seeded login URLs go unchecked**

The new seeded `login_url` is stored without validation, so malformed values are accepted during startup and fail only when `/oauth2/authorize` returns `400 invalid_redirect_uri`. Validating this field with the other `connectApplications` settings would report configuration errors when the seed is loaded.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

A non-string login_url (e.g. a YAML list) passed seed validation and
reached assertAllowedRedirectUri at /oauth2/authorize, where charCodeAt
threw a TypeError instead of a configuration error. Check the type at
seed load alongside audience; host policy stays at authorize because it
depends on runtime allowedRedirectHosts store state.
@gjtorikian
gjtorikian merged commit 6095131 into main Sep 15, 2026
9 checks passed
@gjtorikian
gjtorikian deleted the workgraph/emulate-74c-d7895e59 branch September 15, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Standalone Connect cannot finish because POST /authkit/oauth2/complete is missing

1 participant